﻿using Bit.Art.Core.Models;
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Bit.Art.Distributed;
using System.Collections;

namespace Bit.Art.Engine
{
    public class OrderQueue : IEnumerable, IEnumerable<Order>
    {
        public SortedDictionary<ulong, Order> _pendingOrderQueue;

        private const int NORMAL_ORDER_PRIORITY_RETRY_COUNT = 3;
        private const int MAX_ORDER_PRIORITY_RETRY_COUNT = 1000;
        private Random _pseudoRnd = new Random();
        public int Count { get { return _pendingOrderQueue.Count; } }

        public OrderQueue()
        {
            _pendingOrderQueue = new SortedDictionary<ulong, Order>();
        }

        /// <summary>
        /// 按订单优先级添加一个订单到等待队列
        /// </summary>
        /// <param name="order">待添加订单</param>
        /// <param name="priority">订单的优先级，便于在取消订单的时候根据id可以快速找到订单</param>
        /// <returns>返回是否成功添加</returns>
        /// <remarks>
        /// 调用端下单后，需要缓存订单的优先级，例如将订单id和价格以及该方法返回的优先级放到redis缓存中，便于后面查询订单或者取消订单使用
        /// </remarks>
        public bool Enqueue(Order order, out ulong priority)
        {
            priority = CalcPriorityFor(order);
            int retryCount = 0;
            bool added = false;
            var origOrderPlaced = order.PlacedDate;
            // 如果有相同优先级，则重试三次
            while (!(added = _pendingOrderQueue.TryAdd(priority, order)) && retryCount < NORMAL_ORDER_PRIORITY_RETRY_COUNT)
            {
                IncrementPlacedDateFor(order);
                priority = CalcPriorityFor(order);
                retryCount++;
            }

            // 如果重试三次还有重复的
            if (retryCount >= NORMAL_ORDER_PRIORITY_RETRY_COUNT && !added)
            {
                //s_logger.WarnFormat("当前订单重试{0}次还是有重复优先级，订单号：{1}，市场：{2}，价格：{3}，交易量：{4}，下单时间：{5}，VIPLevel：{6}。",
                //    retryCount, order.Id, order.Market, order.Price, order.Volume, origOrderPlaced, order.VipLevel);
                while (retryCount < MAX_ORDER_PRIORITY_RETRY_COUNT && !(added = _pendingOrderQueue.TryAdd(priority, order)))
                {
                    priority--;
                }

                //if (!added)
                //{
                //    s_logger.ErrorFormat("当前订单达到最大重试次数{0}次还是有重复优先级，放弃订单，订单号：{1}，市场：{2}，价格：{3}，交易量：{4}，下单时间：{5}，VIPLevel：{6}。",
                //        retryCount, order.Id, order.Market, order.Price, order.Volume, origOrderPlaced, order.VipLevel);
                //}
            }

            return added;
        }

        // 按照下单时间增加100纳秒，再尝试计算优先级
        private void IncrementPlacedDateFor(Order order)
        {
            var rnd = _pseudoRnd.Next(100);
            order.PlacedDate = order.PlacedDate.AddTicks(rnd);
        }

        private readonly ulong TICKS_PER_SECOND = (ulong)TimeSpan.FromSeconds(1).Ticks;
        private readonly uint VOLUME_MAGNITUDE = (uint)Math.Pow(10, Math.Abs(Math.Floor(Math.Log10((double)SiteConfiguration.MINIMUM_ORDER_VOLUME))));
        /// <summary>
        /// 根据订单的下单时间，下单用户的注册时间，下单用户的VIP级别，下单用户的下单量和成交量来计算优先级
        /// </summary>
        /// <param name="order"></param>
        /// <returns></returns>
        private ulong CalcPriorityFor(Order order)
        {
            var priority = (ulong)order.PlacedDate.Ticks;
            priority += order.VipLevel * TICKS_PER_SECOND;
            priority += (ulong)order.Volume * VOLUME_MAGNITUDE;
            priority += (ulong)((order.Volume - order.VolumeRemaining) * VOLUME_MAGNITUDE);
            priority += order.UserId;
            return priority;
        }

        /// <summary>
        /// 从挂单等待队列中获取最高优先级的订单
        /// </summary>
        /// <returns>返回最高优先级的订单，或者在没有任何订单的话返回null。</returns>
        public Order Dequeue()
        {
            var pair = _pendingOrderQueue.FirstOrDefault();
            if (pair.Value != null)
            {
                _pendingOrderQueue.Remove(pair.Key);
                return pair.Value;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 返回队列里最前面的订单
        /// </summary>
        /// <returns>如果队列为空，返回null，否则返回最前面的订单！</returns>
        public Order Peek()
        {
            var pair = _pendingOrderQueue.FirstOrDefault();
            return pair.Value;
        }

        /// <summary>
        /// 取消订单
        /// </summary>
        /// <param name="priority">订单的优先级</param>
        /// <returns>如果成功取消，则返回被取消的订单，否则返回null。</returns>
        public Order Remove(ulong priority)
        {
            return _pendingOrderQueue.Remove(priority, out Order order) ? order : null;
        }

        public IEnumerator GetEnumerator()
        {
            return ((IEnumerable<Order>)this).GetEnumerator();
        }

        IEnumerator<Order> IEnumerable<Order>.GetEnumerator()
        {
            return _pendingOrderQueue.Values.GetEnumerator();
        }
    }
}
